// MAPIProfileFunctions.cpp : Collection of useful MAPI functions

#include "stdafx.h"
#include "Error.h"
#include "MAPIProfileFunctions.h"
#include <atlstr.h>
/*#include "ImportProcs.h"*/
/*#include "Editor.h"*/
/*#include "MAPIFunctions.h"*/
/*#include "ExtraPropTags.h"*/


///////////////////////////////////////////////////////////////////////////////
// Function name	: GetMAPISVCPath
// Description	    : This will get the correct path to the MAPISVC.INF file.
// Return type		: void
// Argument         : LPSTR szMAPIDir - Buffer to hold the path to the MAPISVC file.
//                    ULONG cchMAPIDir - size of the buffer

typedef struct
{
	LPTSTR lpszSection;
	LPTSTR lpszKey;
	ULONG ulKey;
	LPTSTR lpszValue;
} SERVICESINIREC;

// $--HrSetProfileParameters----------------------------------------------
// Add values to MAPISVC.INF
// -----------------------------------------------------------------------------

#define PR_MARKER PR_BODY_A
#define MARKER_STRING "MFCMAPI Existing Provider Marker" // STRING_OK
// Walk through providers and add/remove our tag
// bAddMark of true will add tag, bAddMark of false will remove it
HRESULT HrMarkExistingProviders(LPSERVICEADMIN lpServiceAdmin, BOOL bAddMark/*,LPSPropValue lpService,LPSBinary lpEIDBin*/)
{
	HRESULT			hRes = S_OK;
	LPMAPITABLE		lpProviderTable = NULL;

	if (!lpServiceAdmin)
		return MAPI_E_INVALID_PARAMETER;

	SizedSPropTagArray(1, pTagUID) =
	{
		1,
		{
			PR_SERVICE_UID
		}
	};

	EC_H(lpServiceAdmin->GetMsgServiceTable(0, &lpProviderTable));
	if (lpProviderTable)
	{
		LPSRowSet lpRowSet = NULL;
		LPSRow		 lpCurRow = NULL;
		LPSPropValue lpServiceUID = NULL;

		EC_H(HrQueryAllRows(lpProviderTable, (LPSPropTagArray) &pTagUID, NULL, NULL, 0, &lpRowSet));
		if (lpRowSet && lpRowSet->cRows >= 1)
		{
			for (ULONG i = 0;i< lpRowSet->cRows;i++)
			{
				hRes = S_OK;
				lpCurRow = &lpRowSet->aRow[i];

				lpServiceUID = PpropFindProp(
					lpCurRow->lpProps,
					lpCurRow->cValues,
					PR_SERVICE_UID);

				if (lpServiceUID)
				{
					LPPROFSECT lpSect = NULL;
					EC_H(OpenProfileSection(
						lpServiceAdmin,
						&lpServiceUID->Value.bin,
						&lpSect));
					if (lpSect)
					{
						if (bAddMark)
						{
							SPropValue	PropVal;
							PropVal.ulPropTag = PR_MARKER;
							PropVal.Value.lpszA = MARKER_STRING;
							EC_H(lpSect->SetProps(1,&PropVal,NULL));
						}
						else
						{
							SPropTagArray pTagArray = {1,PR_MARKER};
							WC_H(lpSect->DeleteProps(&pTagArray,NULL));
						}
						hRes = S_OK;
						EC_H(lpSect->SaveChanges(0));
						lpSect->Release();
					}
				}
			}

		}				
		if (lpRowSet)
		{
			MAPIFreeBuffer(lpRowSet);
		}
		if (lpProviderTable)
		{
			lpProviderTable->Release();
		}
		
	}
	return hRes;
}
BOOL CheckStringProp(LPSPropValue lpProp, ULONG ulPropType)
{
	if (PT_STRING8 != ulPropType && PT_UNICODE != ulPropType)
	{
		return false;
	}
	if (!lpProp)
	{
		return false;
	}

	if (PT_ERROR == PROP_TYPE(lpProp->ulPropTag))
	{
		return false;
	}

	if (ulPropType != PROP_TYPE(lpProp->ulPropTag))
	{
		return false;
	}

	if (NULL == lpProp->Value.LPSZ)
	{
		return false;
	}

	if (PT_STRING8 == ulPropType && NULL == lpProp->Value.lpszA[0])
	{
		return false;
	}

	if (PT_UNICODE == ulPropType && NULL == lpProp->Value.lpszW[0])
	{
		return false;
	}

	return true;
}

// Returns first provider without our mark on it
HRESULT HrFindUnmarkedProvider(LPSERVICEADMIN lpServiceAdmin, LPSRowSet* lpRowSet/*,LPSBinary lpEIDBin*/)
{
	HRESULT			hRes = S_OK;
	LPMAPITABLE		lpProviderTable = NULL;
	LPPROFSECT		lpSect = NULL;
//	LPSBinary		lpEIDBin	= NULL; // don't free

	if (!lpServiceAdmin) 
		return MAPI_E_INVALID_PARAMETER;

	*lpRowSet = NULL;

	SizedSPropTagArray(1, pTagUID) =
	{
		1,
		{
			PR_SERVICE_UID,
		}
	};

	EC_H(lpServiceAdmin->GetMsgServiceTable(0, &lpProviderTable));
	if (lpProviderTable)
	{
		EC_H(lpProviderTable->SetColumns((LPSPropTagArray)&pTagUID,TBL_BATCH));
		for (;;)
		{
			EC_H(lpProviderTable->QueryRows(1,0,lpRowSet));
			if (S_OK == hRes && *lpRowSet && 1 == (*lpRowSet)->cRows)
			{
				LPSRow		 lpCurRow = NULL;
				LPSPropValue lpServiceUID = NULL;

				lpCurRow = &(*lpRowSet)->aRow[0];

				lpServiceUID = PpropFindProp(
					lpCurRow->lpProps,
					lpCurRow->cValues,
					PR_SERVICE_UID);

				if (lpServiceUID)
				{
					EC_H(OpenProfileSection(
						lpServiceAdmin,
						&lpServiceUID->Value.bin,
						&lpSect));
					if (lpSect)
					{
						SPropTagArray	pTagArray = {1,PR_MARKER};
						ULONG			ulPropVal = 0;
						LPSPropValue	lpsPropVal = NULL;
						
						EC_H_GETPROPS(lpSect->GetProps(&pTagArray,NULL,&ulPropVal,&lpsPropVal));//!(CheckStringProp(lpsPropVal,PROP_TYPE(PR_MARKER)) &&
						
						if (!(CheckStringProp(lpsPropVal,PROP_TYPE(PR_MARKER)) &&
							!strcmp(lpsPropVal->Value.lpszA,MARKER_STRING)))
						{
							// got an unmarked provider - this is our hit
							// Don't free *lpRowSet - we're returning it
							// wipe any error from the GetProps - it was expected
								
							hRes = S_OK; 
							MAPIFreeBuffer(lpsPropVal);
							break;
						}
						MAPIFreeBuffer(lpsPropVal);
						lpSect->Release();
						lpSect = NULL;
					}
				}
				// go on to next one in the loop
				MAPIFreeBuffer(*lpRowSet);
				*lpRowSet = NULL;
			}
			else
			{
				// no more hits - get out of the loop
				MAPIFreeBuffer(*lpRowSet);
				*lpRowSet = NULL;
				break;
			}
		}
 		if (lpSect) 
 			lpSect->Release();

		lpProviderTable->Release();
	}
	return hRes;
}
#define MAPI_FORCE_ACCESS 0x00080000
HRESULT HrAddServiceToProfile(
							  IN LPSTR lpszServiceName, // Service Name
							  IN ULONG_PTR ulUIParam, // hwnd for CreateMsgService
							  IN ULONG ulFlags, // Flags for CreateMsgService
							  IN ULONG cPropVals, // Count of properties for ConfigureMsgService
							  IN LPSPropValue lpPropVals, // Properties for ConfigureMsgService
							  IN LPSTR lpszProfileName) // profile name,
// 							  OUT LPSRowSet       *lpRowSet ,
// 							  OUT LPSPropValue *lpProviderUID
{
	HRESULT			hRes = S_OK;
	LPPROFADMIN		lpProfAdmin = NULL;
	LPSERVICEADMIN	lpServiceAdmin = NULL;
/*	LPPROVIDERADMIN	lpPAdmin = NULL;*/

	LPSRowSet lpStoreRows = NULL;
/*	LPSPropValue lpServiceUID=NULL;*/

/*	LPSPropValue 		lpProviderUID = NULL;*/
	LPSRowSet       lpRowSet=NULL;


	if (!lpszServiceName || !lpszProfileName) 
		return MAPI_E_INVALID_PARAMETER;

	// Connect to Profile Admin interface.
	MAPIAdminProfiles(0, &lpProfAdmin);
	if (!lpProfAdmin) 
		return hRes;

	EC_H(lpProfAdmin->AdminServices(
		(LPTSTR)lpszProfileName,
		_T(""),
		0,
		0,
		&lpServiceAdmin));

	if (lpServiceAdmin)
	{
		// Only need to mark if we plan on calling ConfigureMsgService
		if (lpPropVals)
		{
			// Add a dummy prop to the current providers
			EC_H(HrMarkExistingProviders(lpServiceAdmin,true));
		}

		lpServiceAdmin->CreateMsgService(
			(LPTSTR)lpszServiceName,
			(LPTSTR)lpszServiceName,
			ulUIParam,
			ulFlags);

		if (lpPropVals)
		{
			LPSPropValue lpServiceUID = NULL;
			// Look for a provider without our dummy prop
			EC_H(HrFindUnmarkedProvider(lpServiceAdmin,&lpRowSet));
			// should only have one unmarked row
			if (lpRowSet && lpRowSet->cRows == 1)
			{
				lpServiceUID = PpropFindProp(
					lpRowSet->aRow[0].lpProps,
					lpRowSet->aRow[0].cValues,
					PR_SERVICE_UID);	
				if (lpServiceUID)
				{
					lpServiceAdmin->ConfigureMsgService(
						(LPMAPIUID) lpServiceUID->Value.bin.lpb,
						NULL,
						0,
						cPropVals,
						lpPropVals);
				}
			
				hRes = S_OK;
				// Strip out the dummy prop
				EC_H(HrMarkExistingProviders(lpServiceAdmin,false));

				MAPIFreeBuffer(lpRowSet);
			}
		}
	}
	if (lpServiceAdmin)
	{
		lpServiceAdmin->Release();
		lpServiceAdmin=NULL;
	}
	if (lpProfAdmin)
	{
		lpProfAdmin->Release();
		lpProfAdmin=NULL;
	}
	return hRes;
} // HrAddServiceToProfile
// $--HrCreateProfile---------------------------------------------
// Creates an empty profile.
// -----------------------------------------------------------------------------

HRESULT OpenProfileSection(LPPROVIDERADMIN lpProviderAdmin, LPSBinary lpProviderUID, LPPROFSECT* lppProfSect)
{
	HRESULT			hRes = S_OK;

	if (!lpProviderUID || !lpProviderAdmin || !lppProfSect) return MAPI_E_INVALID_PARAMETER;
	*lppProfSect = NULL;

	WC_H(lpProviderAdmin->OpenProfileSection(
		(LPMAPIUID) lpProviderUID->lpb,
		NULL,
		MAPI_MODIFY | MAPI_FORCE_ACCESS,
		lppProfSect));
	if (!*lppProfSect)
	{
		hRes = S_OK;

		// We only do this hack as a last resort - it can crash some versions of Outlook, but is required for Exchange
		*(((BYTE*)lpProviderAdmin) + 0x60) = 0x2;  // Use at your own risk! NOT SUPPORTED!

		WC_H(lpProviderAdmin->OpenProfileSection(
			(LPMAPIUID) lpProviderUID->lpb,
			NULL,
			MAPI_MODIFY,
			lppProfSect));
	}
	return hRes;
}
HRESULT HrCreateProfile(
						IN LPSTR lpszProfileName) // profile name
{
	HRESULT			hRes = S_OK;
	LPPROFADMIN		lpProfAdmin = NULL;


	if (!lpszProfileName) return MAPI_E_INVALID_PARAMETER;

	// Connect to Profile Admin interface.
	EC_H(MAPIAdminProfiles(0, &lpProfAdmin));
	if (!lpProfAdmin) return hRes;

	// Create the profile
	WC_H(lpProfAdmin->CreateProfile(
		(LPTSTR)lpszProfileName,
		NULL,
		0,
		NULL)); // fMapiUnicode is not supported!
	if (S_OK != hRes)
	{
		// Did it fail because a profile of this name already exists?
		HrMAPIProfileExists(lpProfAdmin, lpszProfileName);
	}

	lpProfAdmin->Release();

	return hRes;
}

// $--HrRemoveProfile---------------------------------------------------------
// Removes a profile.
// ------------------------------------------------------------------------------
HRESULT HrRemoveProfile(
						LPSTR lpszProfileName)
{
	HRESULT		hRes= S_OK;
	LPPROFADMIN	lpProfAdmin = NULL;

	if (!lpszProfileName) return MAPI_E_INVALID_PARAMETER;

	EC_H(MAPIAdminProfiles(0, &lpProfAdmin));
	if (!lpProfAdmin) return hRes;

	EC_H(lpProfAdmin->DeleteProfile((LPTSTR)lpszProfileName, 0));

	lpProfAdmin->Release();

	RegFlushKey(HKEY_LOCAL_MACHINE);
	RegFlushKey(HKEY_CURRENT_USER);

	return hRes;
}

// $--HrMAPIProfileExists---------------------------------------------------------
// Checks for an existing profile.
// -----------------------------------------------------------------------------
HRESULT HrMAPIProfileExists(
							LPPROFADMIN lpProfAdmin,
							LPSTR lpszProfileName)
{
	HRESULT hRes = S_OK;
	LPMAPITABLE lpTable = NULL;
	LPSRowSet lpRows = NULL;
	LPSPropValue lpProp = NULL;
	ULONG i = 0;
	ULONG cRows = 0;

	SizedSPropTagArray(1, rgPropTag) =
	{
		1,
		{
			PR_DISPLAY_NAME_A
		}
	};

	if (!lpProfAdmin || !lpszProfileName) return MAPI_E_INVALID_PARAMETER;

	// Get a table of existing profiles

	EC_H(lpProfAdmin->GetProfileTable(
		0,
		&lpTable));
	if (!lpTable) return hRes;

	EC_H(HrQueryAllRows(
		lpTable,
		(LPSPropTagArray)&rgPropTag,
		NULL,
		NULL,
		0,
		&lpRows));

	cRows = lpRows->cRows;


	if (lpRows)
	{
		if (lpRows->cRows == 0)
		{
			// If table is empty then profile doesn't exist
			hRes = S_OK;
		}
		else
		{
			// Search rows for the folder in question

			if (!FAILED(hRes)) for (i = 0; i < cRows; i++)
			{
				hRes = S_OK;
				lpProp = lpRows->aRow[i].lpProps;

				ULONG ulComp = NULL;

				CompareStringA(
					g_lcid, // LOCALE_INVARIANT,
					NORM_IGNORECASE,
					lpProp[0].Value.lpszA,
					-1,
					lpszProfileName,
					-1);

				if (CSTR_EQUAL == ulComp)
				{
					hRes = E_ACCESSDENIED;
					break;
				}
			}
		}
	}

	if (lpRows) FreeProws(lpRows);

	lpTable->Release();
	return hRes;
}

#define MAPI_FORCE_ACCESS 0x00080000

HRESULT OpenProfileSection(LPSERVICEADMIN lpServiceAdmin, LPSBinary lpServiceUID, LPPROFSECT* lppProfSect)
{
	HRESULT		hRes = S_OK;

	if (!lpServiceUID || !lpServiceAdmin || !lppProfSect) 
		return MAPI_E_INVALID_PARAMETER;
	*lppProfSect = NULL;

	// First, we try the normal way of opening the profile section:
	hRes=lpServiceAdmin->OpenProfileSection(
		(LPMAPIUID) lpServiceUID->lpb,
		NULL,
		MAPI_MODIFY | MAPI_FORCE_ACCESS, 
		lppProfSect);
	if (FAILED(hRes))
	{
		return hRes;
	}

	if (!*lppProfSect)
	{
		hRes = S_OK;
		///////////////////////////////////////////////////////////////////
		// HACK CENTRAL.  This is a MAJOR hack.  MAPI will always return E_ACCESSDENIED
		// when we open a profile section on the service if we are a client.  The workaround
		// (HACK) is to call into one of MAPI's internal functions that bypasses
		// the security check.  We build a Interface to it and then point to it from our
		// offset of 0x48.  USE AT YOUR OWN RISK!  NOT SUPPORTED!
		interface IOpenSectionHack : public IUnknown
		{
			public:
				virtual HRESULT STDMETHODCALLTYPE OpenSection(LPMAPIUID, ULONG, LPPROFSECT*) = 0;
		};

		IOpenSectionHack** ppProfile;

		ppProfile = (IOpenSectionHack**)((((BYTE*)lpServiceAdmin) + 0x48));

		// Now, we want to get open the Services Profile Section and store that
		// interface with the Object

		if (ppProfile && *ppProfile)
		{
			EC_H((*ppProfile)->OpenSection(
				(LPMAPIUID)lpServiceUID->lpb,
				MAPI_MODIFY,
				lppProfSect));
		}
		else
		{
			hRes = MAPI_E_NOT_FOUND;
		}
		// END OF HACK.  I'm amazed that this works....
		///////////////////////////////////////////////////////////////////
	}
	return hRes;
}


HRESULT HrAddPSTToProfile(
						  IN ULONG_PTR ulUIParam, // hwnd for CreateMsgService
						  BOOL bUnicodePST,
						  IN LPTSTR lpszPSTPath, // PST name
						  IN LPTSTR lpszPSTName,
						  IN LPSTR lpszProfileName // profile name
// 						  OUT LPSRowSet       *lpRowSet,
// 						  OUT LPSPropValue *lpProviderUID
						  /* BOOL bPasswordSet, */// whether or not to include a password
						  /* IN LPSTR lpszPassword*/) // password to include
{
	HRESULT			hRes = S_OK;

	if (!lpszPSTPath || !lpszProfileName) 
		return MAPI_E_INVALID_PARAMETER;

	SPropValue PropVal[2];

	PropVal[0].ulPropTag = CHANGE_PROP_TYPE(PR_PST_PATH, PT_TSTRING);
	PropVal[0].Value.LPSZ = lpszPSTPath;
	PropVal[1].ulPropTag = PR_DISPLAY_NAME;
	PropVal[1].Value.LPSZ = lpszPSTName;
//	PropVal[2].ulPropTag =PR_ENTRYID;


	if (bUnicodePST)
	{
		EC_H(HrAddServiceToProfile("MSUPST MS",ulUIParam,NULL,2,PropVal,lpszProfileName)); // STRING_OK
	}
	else
	{
		EC_H(HrAddServiceToProfile("MSPST MS",ulUIParam,NULL,2,PropVal,lpszProfileName)); // STRING_OK
	}

	

	return hRes;
}


